package com.zhou.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.InetAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author lang.zhou
 * @date 2019/12/11
 */
@Slf4j
public class WebUtil {



    /**
     * 向客户端写一段html文本
     */
    public static void writeResponse(HttpServletResponse response, String message){
        writeResponse(response, message,"text/html;charset=UTF-8",true);
    }
    /**
     * 向客户端写一段文本
     */
    public static void writeResponse(HttpServletResponse response, String message,String contentType){
        writeResponse(response, message, contentType,true);
    }
    /**
     * 向客户端写一段文本
     */
    public static void writeResponse(HttpServletResponse response, String message,String contentType,boolean ifClose){
        PrintWriter out= null;
        try{
            response.setContentType(contentType);
            out = response.getWriter();
            out.print(message);
            out.flush();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(ifClose){
                IOUtils.closeQuietly(out);
            }
        }
    }
    public static void writeObject(HttpServletResponse response, Object o){
        PrintWriter out= null;
        try{
            response.setContentType("application/json");
            out = response.getWriter();
            out.println(JSON.toJSONString(o, SerializerFeature.DisableCircularReferenceDetect));
            out.flush();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            IOUtils.closeQuietly(out);
        }

    }
    /**
     * 批量设置request  Attribute
     */
    public static void setAttribute(HttpServletRequest request, Map<String,Object> map){
        for(String key : map.keySet()){
            request.setAttribute(key,map.get(key));
        }
    }
    /**
     * 将url中参数封装成map
     * @param queryString request.getQueryString()
     */
    public static Map<String, String> getQueryStringMap(String queryString) {
        Map<String, String> map = new HashMap<>();
        if (StringTool.isNotEmpty(queryString)) {
            String[] arr = queryString.split("&");
            for (String s : arr) {
                String[] arrs = s.split("=");
                if(arrs.length > 0){
                    String key = arrs[0];
                    String value = null;
                    if(arrs.length > 1){
                        value = arrs[1];
                    }
                    map.put(key, StringTool.safeToString(value,""));
                }
            }
        }
        return map;
    }
    public static String getRequestParameter(HttpServletRequest request, String key , String dv){
        return StringTool.safeToString(request.getParameter(key),dv);
    }
    public static String encodeURI(String str,String charset){
        try {
            return URLEncoder.encode(str,charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
    public static String encodeURI(String str){
        return URLEncoder.encode(str);
    }
    public static String decodeURI(String str,String charset){
        try {
            return URLDecoder.decode(str,charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
    public static String decodeURI(String str){
        return URLDecoder.decode(str);
    }
    /**
     * 向客户端下载文件
     */
    public static void download(HttpServletResponse response, String filePathName, String displayName) throws IOException {
        FileInputStream in = null;
        File d = new File(filePathName);
        try{
            response.setHeader("Content-Length", String.valueOf(d.length()));
            in = new FileInputStream(d);
            download(response,in,displayName);
        }finally {
            IOUtils.closeQuietly(in);
        }
    }
    public static void download(HttpServletResponse response, File file) throws IOException {
        FileInputStream in = null;
        try{
            response.setHeader("Content-Length", String.valueOf(file.length()));
            in = new FileInputStream(file);
            download(response,in,file.getName());
        }finally {
            IOUtils.closeQuietly(in);
        }
    }
    /**
     * 向客户端下载文件
     */
    public static void writeStream(HttpServletResponse response, InputStream in) throws IOException{
        OutputStream out = null;
        try{
            out = response.getOutputStream();
            IOUtils.copy(in,out);
        }finally {
            IOUtils.closeQuietly(out);
        }
    }

    public static String encodeFileName(String fileNames, HttpServletRequest request) {
        String codedFilename = null;
        if (request == null) {
            log.warn("encodeFileName中传递了空的HttpServletRequest,导致无法根据浏览器类型兼容中文乱码");
            return fileNames;
        } else {
            try {
                String agent = request.getHeader("USER-AGENT");
                if ((null == agent || !agent.contains("MSIE")) && (null == agent || !agent.contains("Trident")) && (null == agent || !agent.contains("Edge")) && (null == agent || !agent.contains("Mozilla"))) {
                    return fileNames;
                }

                codedFilename = URLEncoder.encode(fileNames, "UTF-8").replaceAll("\\+", "%20").replaceAll("%2B", "\\+");
            } catch (Exception var4) {
                log.warn("encodeFileName失败", var4);
            }

            return codedFilename;
        }
    }

    public static String getDisposition(String fileName, boolean open) {
        HttpServletRequest request = currentRequest();
        String encodeFileName = encodeFileName(fileName, request);
        return open ? "filename=\"" + encodeFileName + "\"" : "attachment; filename=\"" + encodeFileName + "\"; filename*=utf-8''" + encodeFileName;
    }

    public static void download(HttpServletResponse response, InputStream in, String displayName) throws IOException{
        download(response, in, displayName,false);
    }
    public static void download(HttpServletResponse response, InputStream in, String displayName,boolean open) throws IOException{
        OutputStream out=null;
        try{
            /*String fileName = URLEncoder.encode(displayName.trim(), "UTF-8");

            //对空格进行替换
            fileName = fileName.replaceAll("\\+","%20");
            //对+号进行替换,Chrome无法转义带有%2B
            fileName = fileName.replaceAll("%2B","+");
            //对(号进行替换,Chrome无法转义带有%28
            fileName = fileName.replaceAll("%28","(");
            //对)号进行替换,Chrome无法转义带有%29
            fileName = fileName.replaceAll("%29",")");*/
            // 定义输出类型(下载)
            response.setContentType(getContentType(displayName));

            String encodeFileName = encodeFileName(displayName, currentRequest());
            // 定义输出文件头
            response.setHeader("Content-Disposition", getDisposition(displayName, open));
            response.setHeader("Pragma", "public");
            response.setHeader("fileName", encodeFileName);
            response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
            response.setHeader("Cache-Control", "public");
            response.setHeader("Expires", "0");
            response.setHeader("Content-Transfer-Encoding", "binary");
            response.setHeader("Content-Length", String.valueOf(in.available()));
            out=response.getOutputStream();
            byte[] buffer=new byte[1024];
            int i;
            while((i=in.read(buffer))!=-1){
                out.write(buffer, 0, i);
            }

        }finally {
            IOUtils.closeQuietly(out);
        }

    }

    public static String getContentType(String fileName) {
        String result = null;
        int i = fileName == null?-1:fileName.lastIndexOf(46);
        if(i >= 0) {
            String key = fileName.substring(i + 1).toLowerCase();
            Map<String, String> contentTypeMap = new HashMap<>(10);
            contentTypeMap.put("txt", "text/plain");
            contentTypeMap.put("htm", "text/html");
            contentTypeMap.put("html", "text/html");
            contentTypeMap.put("xml", "text/xml");
            contentTypeMap.put("rtf", "text/richtext");
            contentTypeMap.put("doc", "application/msword");
            contentTypeMap.put("xls", "application/x-msexcel");
            contentTypeMap.put("ppt", "application/x-mspowerpoint");
            contentTypeMap.put("pdf", "application/pdf");
            contentTypeMap.put("bmp", "image/bmp");
            contentTypeMap.put("jpg", "image/jpeg");
            contentTypeMap.put("gif", "image/gif");
            contentTypeMap.put("exe", "application/octet-stream");
            result = contentTypeMap.get(key);
        }

        return result == null?"application/unknown":result;
    }

    /**
     * 下载多个文件并压缩
     */
    /*public static void downloadWorkbook(HttpServletResponse response, Workbook workbook, String fileName) throws Exception{
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        workbook.write(out);
        WebUtil.download(response,new ByteArrayInputStream(out.toByteArray()),fileName);
    }*/
    /**
     * 下载多个文件并压缩
     */
    public static void downloadZip(HttpServletResponse response, String fileName, List<String> fileList) throws Exception{
        List<File> list = new ArrayList<>(fileList.size());
        fileList.forEach(s->{
            list.add(new File(s));
        });
        downloadZipFile(response,fileName,list);
    }
    public static void downloadZipFile(HttpServletResponse response, String fileName, List<File> fileList) throws Exception{

        InputStream ins = null;
        //  获取文件输出IO流
        OutputStream outs = null;
        ZipOutputStream out = null;
        try {
            //o = new FileOutputStream(fileName);
            response.setContentType("application/x-download");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + new String(fileName.getBytes("GB2312"), "ISO8859-1"));
            //  获取文件输出IO流
            outs = response.getOutputStream();
            // 创建zip文件
            out = new ZipOutputStream(outs);
            for (File file : fileList) {

                FileInputStream in = null;
                try{
                    in = new FileInputStream(file);
                    // 添加压缩包文件
                    out.putNextEntry(new ZipEntry(file.getName()));

                    IOUtils.copy(in,out);
                    // 关闭入口
                    out.closeEntry();
                }finally {
                    IOUtils.closeQuietly(in);
                }

            }
            outs.flush();
            out.close();
        }finally {
            IOUtils.closeQuietly(out);
            IOUtils.closeQuietly(outs);
            IOUtils.closeQuietly(ins);
        }
    }

    public static void addCookie(HttpServletResponse response, String name, String value, int age) {
        addCookie(response, name, value, age, "",true);
    }

    public static void addCookie(HttpServletResponse response, String name, String value, int age,String path,boolean httpOnly) {
        StringBuilder builder = new StringBuilder();
        builder.append(name).append("=").append(value).append(";");
        if(age > 0) {
            builder.append("max-age=").append(age).append(";");
        } else if(age == 0) {
            builder.append("max-age=").append(0).append(";");
        }

        builder.append("path=");
        if(path == null) {
            builder.append("/");
        } else {
            if(!path.startsWith("/")) {
                builder.append("/");
            }

            builder.append(path);
        }
        builder.append(";");
        if(httpOnly) {
            builder.append("Secure;");
        }
        builder.append("HttpOnly");
        response.addHeader("Set-Cookie", builder.toString());
    }

    public static String getCookie(HttpServletRequest request, String name) {
        String result = null;
        Cookie[] cookies = request.getCookies();
        if(cookies != null && cookies.length > 0) {
            for (Cookie c : cookies) {
                if (c.getName().equals(name)) {
                    result = c.getValue();
                    break;
                }
            }
        }
        return result;
    }
    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");

        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值，第一个ip才是真实ip
            if(ip.contains(",")){
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return  "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

    /**
     * 获取本地IP地址
     */
    public static String getHostIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            //ignore
        }
        return "127.0.0.1";
    }

    /**
     * 获取主机名
     */
    public static String getHostName() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            //ignore
        }
        return "未知";
    }
    /**
     * 获取操作系统信息
     */
    public static String getOsInfo(HttpServletRequest request){
        String  userAgent       = request.getHeader("User-Agent");
        String os;
        if (userAgent.toLowerCase().contains("windows"))
        {
            os = "Windows";
        } else if(userAgent.toLowerCase().contains("mac"))
        {
            os = "Mac";
        } else if(userAgent.toLowerCase().contains("x11"))
        {
            os = "Unix";
        } else if(userAgent.toLowerCase().contains("android"))
        {
            os = "Android";
        } else if(userAgent.toLowerCase().contains("iphone"))
        {
            os = "IPhone";
        }else{
            os = "UnKnown, More-Info: "+userAgent;
        }
        return os;
    }
    /**
     * 获取浏览器及浏览器版本信息
     */
    public static String getBrowserInfo(HttpServletRequest request){
        String  userAgent       = request.getHeader("User-Agent");
        String  user            =   userAgent.toLowerCase();
        String browser = "";
        if (user.contains("edge"))
        {
            browser=(userAgent.substring(userAgent.indexOf("Edge")).split(" ")[0]).replace("/", "-");
        } else if (user.contains("msie"))
        {
            String substring=userAgent.substring(userAgent.indexOf("MSIE")).split(";")[0];
            browser=substring.split(" ")[0].replace("MSIE", "IE")+"-"+substring.split(" ")[1];
        } else if (user.contains("safari") && user.contains("version"))
        {
            browser=(userAgent.substring(userAgent.indexOf("Safari")).split(" ")[0]).split("/")[0]
                    + "-" +(userAgent.substring(userAgent.indexOf("Version")).split(" ")[0]).split("/")[1];
        } else if ( user.contains("opr") || user.contains("opera"))
        {
            if(user.contains("opera")){
                browser=(userAgent.substring(userAgent.indexOf("Opera")).split(" ")[0]).split("/")[0]
                        +"-"+(userAgent.substring(userAgent.indexOf("Version")).split(" ")[0]).split("/")[1];
            }else if(user.contains("opr")){
                browser=((userAgent.substring(userAgent.indexOf("OPR")).split(" ")[0]).replace("/", "-"))
                        .replace("OPR", "Opera");
            }

        } else if (user.contains("chrome"))
        {
            browser=(userAgent.substring(userAgent.indexOf("Chrome")).split(" ")[0]).replace("/", "-");
        } else if ((user.contains("mozilla/7.0")) || (user.contains("netscape6"))  ||
                (user.contains("mozilla/4.7")) || (user.contains("mozilla/4.78")) ||
                (user.contains("mozilla/4.08")) || (user.contains("mozilla/3")) )
        {
            browser = "Netscape-?";

        } else if (user.contains("firefox"))
        {
            browser=(userAgent.substring(userAgent.indexOf("Firefox")).split(" ")[0]).replace("/", "-");
        } else if(user.contains("rv"))
        {
            String IEVersion = (userAgent.substring(userAgent.indexOf("rv")).split(" ")[0]).replace("rv:", "-");
            browser="IE" + IEVersion.substring(0,IEVersion.length() - 1);
        } else
        {
            browser = "UnKnown, More-Info: "+userAgent;
        }
        return browser;
    }

    /*public static Map<String, String> getParameterMap(HttpServletRequest request) {
        Map<String, String> result = new HashMap<>();
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String parameterName = parameterNames.nextElement();
            result.put(parameterName, request.getParameter(parameterName));
        }
        return result;
    }*/

    /**
     * 获得所有请求参数
     */
    public static Map<String, Object> getParameterMap(HttpServletRequest request) {
        String contentType = StringTool.safeToString(request.getHeader("Content-Type"),"").toLowerCase();
        Map<String, Object> params = new HashMap<>();
        if(contentType.contains("application/json")){
            StringBuilder sb = new StringBuilder();
            String line;
            try {
                BufferedReader reader = request.getReader();
                while ((line = reader.readLine()) != null){
                    sb.append(line);
                }
            } catch (Exception e) {
                //ignore
            }
            //将空格和换行符替换掉避免使用反序列化工具解析对象时失败
            String jsonString = sb.toString().replaceAll("\\s","").replaceAll("\n","");
            try {
                params = JSON.parseObject(jsonString);
            } catch (Exception e) {
                log.error("参数转换失败");
            }
        }else{
            for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
                params.put(entry.getKey(), StringTool.join(entry.getValue(), ","));
            }
        }
        return params;
    }

    public static HttpServletRequest currentRequest() {
        HttpServletRequest request = null;
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        if(servletRequestAttributes != null){
            request = servletRequestAttributes.getRequest();
        }
        return request;
    }

    public static HttpServletResponse currentResponse() {
        HttpServletResponse response = null;
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        if(servletRequestAttributes != null){
            response = servletRequestAttributes.getResponse();
        }
        return response;
    }

    /**
     * 补全base64缺失等号
     */
    public static String fixBase64(String s) {
        switch(s.length()%4) {
            case 3:
                s+= "==="; break; // 注：其实只需要补充一个或者两个等号，不存在补充三个等号的情况
            case 2:
                s+= "=="; break;
            case 1:
                s+= "="; break;
            default:
        }
        return s;
    }
    /**
     * 去掉首尾斜杠
     */
    public static String formatWebPath(String s){
        if(s != null && s.trim().length() > 0){
            s = s.trim().replace("\\","/");
            if(s.endsWith("/")){
                s = s.substring(0,s.length() -1);
            }
            if(s.startsWith("/")){
                s = s.substring(1);
            }
            return s;
        }
        return "";
    }
}
